define('framework/directive/ControlValidatorServiceConstructor', [
    'angular',
    'jquery',
    'framework/directive/ControlValidator'
], function (angular, $, ControlValidator) {
    return function (Arrays, $http, $timeout) {
        var prototypeRef = ControlValidator.prototype;

        prototypeRef.isNoControlProperty = function (property) {
            return property && !this.form.hasOwnProperty(property) && property.indexOf('.') !== -1;
        };

        prototypeRef.getMatchedControls = function () {
            var me = this,
                form = me.form,
                property = me.property,
                matchedProperties = angular.copy(me.properties) || [];
            if (me.isNoControlProperty(me.property)) {
                angular.forEach(form, function (v, k) {
                    if (k.match(new RegExp(me.property.split('.').join('(\\\[\\\d+\\\])?\.')))) {
                        matchedProperties.push(k);
                    }
                });
            } else if (angular.isString(property) && !!property) {
                matchedProperties.push(property);
            } else if (angular.isArray(matchedProperties) && matchedProperties.length > 0) {
                if (angular.isString(me.property)) {
                    matchedProperties.push(me.property);
                }
            }
            return Arrays.transform(matchedProperties, function (property) {
                return form[property];
            });
        };

        prototypeRef.apply = function (predicatedCallback) {
            var me = this,
                form = me.form,
                matchedControls = me.matchedControls,
                ruleName = me.ruleName, control, rulePredicate;

            angular.forEach(matchedControls, function (matchedControl) {
                if (matchedControl) {
                    var willSetValidityControls = me.getWillSetValidityControls(matchedControl);
                    rulePredicate = me[ruleName];
                    matchedControl.$parsers.unshift(function (value) {
                        // 异步验证， 需要传入control
                        if (ruleName === 'unique') {
                            rulePredicate.call(me, value, willSetValidityControls);
                        } else {
                            me.defaultControlValidCallback(rulePredicate.call(me, value), willSetValidityControls);
                        }
                        return value;
                    });
                    matchedControl.$formatters.unshift(function (value) {
                        // 异步验证， 需要传入control
                        if (ruleName === 'unique') {
                            rulePredicate.call(me, value, willSetValidityControls);
                        } else {
                            var isValid = rulePredicate.call(me, value);
                            me.defaultControlValidCallback(isValid, willSetValidityControls);
                        }
                        return value;
                    });
                }
            });
        };

        prototypeRef.getWillSetValidityControls = function (matchedControl) {
            var me = this,
                form = me.form,
                property = me.property,
                properties = me.properties;
            if (!!property && form.hasOwnProperty(property)) {
                return [form[property]];
            } else if (!!property && !form.hasOwnProperty(property) && !!matchedControl) {
                return [matchedControl];
            } else {
                return Arrays.transform(properties, function (property) {
                    return form[property];
                });
            }
        };

        prototypeRef.defaultControlValidCallback = function (isValid, controls) {
            var ruleName = this.ruleName;
            angular.forEach(controls, function (control) {
                if (control) {
                    control.$setValidity(ruleName, isValid);
                }
            });
        };

        prototypeRef.notNull = function (rawValue) {
            return angular.isDefined(rawValue) && rawValue !== null;
        };

        prototypeRef.notEmpty = function (rawValue) {
            if (this.notNull(rawValue)) {
                if (angular.isString(rawValue) || angular.isArray(rawValue)) {
                    return rawValue.length > 0;
                } else {
                    throw new TypeError('NotEmpty 只支持 String 或 Array 类型的值的验证');
                }
            }
            return false;
        };

        prototypeRef.notBlank = function (rawValue) {
            return !rawValue || (angular.isString(rawValue) && $.trim(rawValue).length > 0);
        };

        prototypeRef.require = function (rawValue) {
            if (angular.isString(rawValue)) {
                return rawValue.length > 0;
            } else {
                return this.notNull(rawValue);
            }
        };

        prototypeRef.pattern = function (rawValue) {
            return !rawValue || (new RegExp(this.rule.pattern).test(rawValue));
        };

        prototypeRef.min = function (rawValue) {
            var value = Number(rawValue);
            return !rawValue || (angular.isNumber(value) && value >= this.rule.min);
        };

        prototypeRef.max = function (rawValue) {
            var value = Number(rawValue);
            return !rawValue || (angular.isNumber(value) && value <= this.rule.max);
        };

        prototypeRef.length = function (rawValue) {
            return !rawValue || (angular.isString(rawValue) && rawValue.length <= this.rule.max && rawValue.length >= this.rule.min);
        };

        prototypeRef.numeric = function (rawValue) {
            var value, token;
            if (rawValue) {
                value = Number(rawValue);
                token = rawValue.split('.');

                if (this.rule.precision >= 0 && (token[1] || '').length > this.rule.precision) {
                    return false;
                } else if (this.rule.sign === 'POSITIVE' && value < 0) {
                    return false;
                } else if (this.rule.sign === 'NEGATIVE' && value > 0) {
                    return false;
                }
            }
            return true;
        };

        prototypeRef.range = function (rawValue) {
            var value = Number(rawValue);
            return !rawValue || (angular.isNumber(value) && value >= this.rule.min && value <= this.rule.max);
        };

        prototypeRef.allMatch = function (value) {
            var me = this,
                form = me.form,
                properties = Arrays.filter(me.properties, function (property) {
                    return !!form[property];
                }),
                i, len, property;
            for (i = 0, len = properties.length; i < len; i++) {
                property = properties[i];
                if (value !== form[property].$viewValue) {
                    return false;
                }
            }
            return true;
        };

        prototypeRef.compare = function (rawValue) {
            var rule = this.rule,
                than = rule.than,
                compareToVal = this.form[rule.compareTo].$viewValue;
            if (rawValue && compareToVal) {
                if (than === 'GREATER' && rawValue < compareToVal) {
                    return false;
                } else if (than === 'LESS' && rawValue > compareToVal) {
                    return false;
                }
            }
            return true;
        };

        prototypeRef.unique = function (value, controls) {
            var me = this,
                entityName = me.entityName,
                property = me.property;
            if (value) {
                if (me.reValidTimer) {
                    $timeout.cancel(me.reValidTimer);
                    me.reValidTimer = undefined;
                }
                me.reValidTimer = $timeout(function () {
                    $http.get((window.basePath || '') + '/validation/exists/' + entityName + '/' + property + '/' + encodeURIComponent(value))
                        .success(function (result) {
                            me.defaultControlValidCallback(result && result.success === true, controls);
                            me.reValidTimer = undefined;
                        });
                }, 300);
            } else {
                me.defaultControlValidCallback(true, controls)
            }
        };

        prototypeRef.eitherNotBlank = function (value) {
            var me = this,
                properties = me.properties,
                form = me.form,
                existsControlProperties = Arrays.filter(properties, function (property) {
                    return !!form[property];
                }),
                existsControls = Arrays.transform(existsControlProperties, function (property) {
                    return form[property];
                }),
                values = Arrays.transform(existsControls, function (control) {
                    return control.$viewValue;
                }),
                notBlankValue = Arrays.findOne(values, function (value) {
                    return angular.isString(value) && $.trim(value).length > 0;
                });
            return notBlankValue !== undefined;
        };

        return {
            from: function (validationParams) {
                return new ControlValidator(validationParams);
            },

            isSupported: function (ruleName) {
                return angular.isFunction(ControlValidator.prototype[ruleName]) ;
            }
        }
    };
})
;